Executive Summary

Abstract

Introduction

Methods

Data Collection

Data was imported using the \data_gathering.RMD script. See that script for details of collection.

pander(twitter_summary_stats)

----------------------------------------------------------------------
    Company      Twitter_Followers   Twitter_Statuses   Twitter_Likes 
--------------- ------------------- ------------------ ---------------
  Labatt USA           18535               2816             13417     

Molson Canadian        17258               4541             8784      

Michelob ULTRA         2931                2931             41684     

   Bud Light           17758              17758             13085     
----------------------------------------------------------------------

Table: Table continues below

 
----------------------------------------------
 Twitter_Retweets   Twitter_EngagementPerUser 
------------------ ---------------------------
       8645                   1.19            

       4287                  0.7574           

      15411                   1.046           

       5235                  0.1146           
----------------------------------------------
pander(summary_stats)

----------------------------------------------------------
    Company      Comments   Likes    Shares   Total.Posts 
--------------- ---------- -------- -------- -------------
  Labatt USA       6717     127377   27884       1315     

Molson Canadian    7170     60077    10678        517     

Michelob ULTRA    116516   4614127   254690      3484     

   Bud Light      531451   20137767 1878365      6927     
----------------------------------------------------------

Data Shaping

Taking in raw data and adding a parseable timestamp while filtering on the date and client_ids.

Function Definition

Define functions to create posts per day of week graphs, and timeseries of engagement line graphs.

Additional Data Shaping for Engagement

Shape data into vertical data formats.


Attaching package: ‘chron’

The following objects are masked from ‘package:lubridate’:

    days, hours, minutes, seconds, years

Results

Summary Statistics

  • Lets start here with a table of summary statistics
[1] "tbl_df"     "tbl"        "data.frame"

Matrices plots of Engagement

First plot is aggregated engagement by content type. Second plot, it engagement by type for client(Labatt).

  • As Bud Light and Michelob ULTRA are the to companies with the highest engagement, comparison of

  • Looking at the engagement by content type we see that Labatt is garnering its most significant engagment on Photos, Video, and Links.

  • [ ] TODO: we need to compare posting activity with engagement activity (scatter plot)

Summary Plots

Horizontal stacked bar chart for total engagement comparison of all companies

reorder_size <- function(x) {
  factor(x, levels = names(sort(table(x))))
}
p <- summary_stats %>%
  filter(Engagement != "Total.Posts") %>%
  ggplot(., aes(x = Company, y = log(Number), fill = Engagement)) +
  geom_bar(stat = "identity") +
  xlab('Brand') + ylab('Engagement(Scaled)') +
  ggtitle('Logarithmic Transformation of Total Engagement') +
  coord_flip()
plot(p)

Day of Week

Total posts per day of the week.

# without brand ID these are uninformative
for(i in seq_along(df_names)) {
  p <- day_of_week(df_names[i], client_names[i])
  plot(p)
}

p <- ggplot(data = all_companies_ts, aes(x = wday(timestamp, label = TRUE))) +
  geom_bar(aes(fill = ..count..)) +
  theme(legend.position = "none") +
  xlab("Day of the Week") + ylab("Number of Posts") +
  scale_fill_gradient(low = "midnightblue", high = "aquamarine4") + 
  facet_wrap(~from_name, ncol = 4) +
  ggtitle("Daily Posting Activity by Brand(Facebook)")
plot(p)

  • What is the total number of posts?
dowDat <- select(all_companies_ts, total_engagement,from_name, timestamp)
dowDat$dow <- wday(dowDat$timestamp, label=TRUE)
dowDat <- aggregate(total_engagement~dow+from_name, data=dowDat, FUN=mean)
p <- ggplot(dowDat, aes(x = dow, y = total_engagement)) +
  geom_bar(stat="identity", aes(fill = total_engagement)) + 
  facet_grid(~from_name) + 
  ggtitle('Engagements Per Day of Week(Facebook)') +
  theme(legend.position = "none") +
  xlab("Day of the Week") + ylab("Number of Engagements") +
  scale_fill_gradient(low = "midnightblue", high = "aquamarine4")
plot(p)

-[ ] TODO: Create a plot for Post by engagement graphics (scatter plot). To answer the question on days with lots of posts do we get lots of engagment.

mdat <- all_companies_ts
mdat$month <- format(as.POSIXct(mdat$timestamp), '%m')
mdat %>%
  ggplot(aes(month, log(total_engagement))) +
  geom_boxplot() +
  ggtitle('Engagment grouped by Month(Facebook)') + ylab('Engagement') + xlab('Month') +
  facet_grid(from_name ~ ., scales = "free")

  • [] TODO: With that data we can ask what posts get the most engagment, we can look at top engagment and bottom engagements posts and what qualities they share or differ by.

Engagement by Time of Day (TOD)

Timeseries Engagement

Plots for the timeseries engagement line.

for(i in seq_along(df_names)) {
  p <- timeseries_engagement(client_names_proper[i])
  plot(p)
}

Initial Visualization of engagement over time on a line

all_companies_ts <- all_companies_ts %>%
  filter(from_id %in% client_ids) %>%
  mutate(month = as.Date(cut(all_companies_ts$timestamp, breaks = "month")))
all_companies_ts %>%
  select(from_name, month, total_engagement) %>%
  group_by(from_name,month) %>%
  summarise(totEng = sum(total_engagement)) %>%
  ggplot(., aes(x = month, y = totEng)) +
  ylab('Total Engagements') + xlab('Years') +
  geom_point(aes(color = from_name)) + ylim(0, 2200000) +
  ggtitle('Engagement Over Time(Facebook)') +
  geom_smooth(aes(color = from_name), se = FALSE)

all_companies_ts %>%
  select(from_name, month, total_engagement, timestamp) %>%
  filter(from_name != "Bud Light" ) %>%
  filter(from_name != "Michelob ULTRA") %>%
  filter(year(timestamp) %in% c('2015', '2016')) %>%
  group_by(from_name,month) %>%
  summarise(totEng = sum(total_engagement)) %>%
  ggplot(., aes(x = month, y = totEng)) +
   geom_point(aes(color = from_name)) +
   geom_smooth(aes(color = from_name), se = FALSE) +
   ggtitle("Monthly Facebook Engagement Labatt vs Molson")

  • This is an interesting drop of ~30% over the first 6 months of 2015. The brand has still not recovered from that reduction.
  • What is different about the content during this period?

  • Might be valuable to look back at the entire timeseries for periods of distinct dynamism.

Labatt Wordclouds

Removed filter because labatt does not have significant inflection point whereas previous analysis

labatt$timestamp <- date(labatt$timestamp)
labatt_clean_pre <- str_replace_all(labatt$message, "@\\w+", "")
labatt_clean_pre <- gsub("&amp", "", labatt_clean_pre)
labatt_clean_pre <- gsub("(RT|via)((?:\\b\\W*@\\w+)+)", "", labatt_clean_pre)
labatt_clean_pre <- gsub("@\\w+", "", labatt_clean_pre)
labatt_clean_pre <- gsub("[[:punct:]]", "", labatt_clean_pre)
labatt_clean_pre <- gsub("[[:digit:]]", "", labatt_clean_pre)
labatt_clean_pre <- gsub("http\\w+", "", labatt_clean_pre)
labatt_clean_pre <- gsub("[ \t]{2,}", "", labatt_clean_pre)
labatt_clean_pre <- gsub("^\\s+|\\s+$", "", labatt_clean_pre)
labatt_corpus_pre <- Corpus(VectorSource(labatt_clean_pre))
labatt_corpus_pre <- tm_map(labatt_corpus_pre, removePunctuation)
labatt_corpus_pre <- tm_map(labatt_corpus_pre, content_transformer(tolower))
labatt_corpus_pre <- tm_map(labatt_corpus_pre, removeWords, stopwords("english"))
labatt_corpus_pre <- tm_map(labatt_corpus_pre, removeWords, c("amp", "2yo", "3yo", "4yo"))
labatt_corpus_pre <- tm_map(labatt_corpus_pre, stripWhitespace)
pal <- brewer.pal(9,"YlGnBu")
pal <- pal[-(1:4)]
set.seed(123)
wordcloud(words = labatt_corpus_pre, scale=c(5,0.1), max.words=25, random.order=FALSE, 
          rot.per=0.35, use.r.layout=FALSE, colors=pal)

Point Graphs for Posts

Displays engagement per post to find outliers.

p <- ggplot(all_companies_ts, aes(x = month, y = total_engagement)) +
  geom_point(aes(color = from_name)) +
  xlab("Year") + ylab("Total Engagement") + 
  theme(legend.title=element_blank(), 
        legend.text=element_text(size=12), 
        legend.position=c(0.18, 0.77), 
        legend.background=element_rect(fill=alpha('gray', 0)))
plot(p)

Total Engagement Line

# q <- aggregate(all_companies_ts$total_engagement~all_companies_ts$month+
#                  all_companies_ts$from_name,
#                FUN=sum)
# 
# ggplot(q, aes(x = q$`all_companies_ts$month`, y = q$`all_companies_ts$total_engagement`)) +
#   geom_line(aes(color=q$`all_companies_ts$from_name`)) +
#   ylab("Total Engagement") + xlab("Year") +
#   theme(legend.title=element_blank(), 
#         legend.text=element_text(size=12), 
#         legend.position=c(0.18, 0.77), 
#         legend.background=element_rect(fill=alpha('gray', 0)))

Engagement by Company

### molson Content Over Time ###
t <- all_companies_ts %>%
  filter(., from_name == "Molson Canadian")
t <- data.frame(table(t$month, t$type))
t$Var1 <- date(t$Var1)
ggplot(t, aes(x = Var1, y = Freq, group = Var2)) +
  geom_line(aes(color=Var2)) +
  ggtitle('Molson Engagement(Facebook)') +
  xlab("Year") + ylab("Post Frequency") +
  theme(legend.title=element_blank(), 
        legend.text=element_text(size=12), 
        legend.position=c(0.18, 0.77), 
        legend.background=element_rect(fill=alpha('gray', 0)))

#TRISTEN'S GRAPHS!!
#Labatt Content Over Time
### Labatt Content Over Time ###
t <- all_companies_ts %>%
  filter(., from_name == "Labatt USA")
t <- data.frame(table(t$month, t$type))
t$Var1 <- date(t$Var1)
ggplot(t, aes(x = Var1, y = Freq, group = Var2)) +
  geom_line(aes(color=Var2)) +
  ggtitle('Labatt Facebook Activity(Facebook)') +
  xlab("Year") + ylab("Post Frequency") +
  theme(legend.title=element_blank(), 
        legend.text=element_text(size=12), 
        legend.position=c(0.18, 0.77), 
        legend.background=element_rect(fill=alpha('gray', 0)))

#Labatt Content Over Time
#MichelobULTRA Content Over Time ###
t <- all_companies_ts %>%
  filter(., from_name == "Michelob ULTRA")
t <- data.frame(table(t$month, t$type))
t$Var1 <- date(t$Var1)
ggplot(t, aes(x = Var1, y = Freq, group = Var2)) +
  geom_line(aes(color=Var2)) +
  ggtitle('Michelob ULTRA Engagement(Facebook)') +
  xlab("Year") + ylab("Post Frequency") +
  theme(legend.title=element_blank(), 
        legend.text=element_text(size=12), 
        legend.position=c(0.18, 0.77), 
        legend.background=element_rect(fill=alpha('gray', 0)))

  • Is this true? TODO: Verify that these are the only content types for Molson.
#Labatt Content Over Time
#Bud Light Content Over Time ###
t <- all_companies_ts %>%
  filter(., from_name == "Bud Light")
t <- data.frame(table(t$month, t$type))
t$Var1 <- date(t$Var1)
ggplot(t, aes(x = Var1, y = Freq, group = Var2)) +
  geom_line(aes(color=Var2)) +
  ggtitle('Bud Light Engagement(Facebook)') +
  xlab("Year") + ylab("Post Frequency") +
  theme(legend.title=element_blank(), 
        legend.text=element_text(size=12), 
        legend.position=c(0.18, 0.77), 
        legend.background=element_rect(fill=alpha('gray', 0)))

Pulling #hastags

I found an example on Stackoverflow

Experiment with Hashtag extraction

# LabattUSA_timeline %>% 
#   filter()
# 
# 
# tweets <- LabattUSA_timeline$text
# match <- regmatches(tweets,gregexpr("#[[:alnum:]]+",tweets))
# 
# # Convert the list to a corpus
# # new_corpus <- as.VCorpus(new_list)  from Stackoverflow (http://stackoverflow.com/questions/34061912/how-transform-a-list-into-a-corpus-in-r)
# 
# new_corpus <- as.VCorpus(match)
# class(new_corpus)
# inspect(new_corpus)
# 
# EnsurePackage <- function(x) {
#   # EnsurePackage(x) - Installs and loads a package if necessary
#   # Args:
#   #   x: name of package
# 
#   x <- as.character(x)
#   if (!require(x, character.only=TRUE)) {
#     install.packages(pkgs=x, repos="http://cran.r-project.org")
#     require(x, character.only=TRUE)
#   }
# }
# 
# MakeWordCloud <- function(corpus) {
#   # Make a word cloud
#   #
#   # Args:
#   #   textVec: a text vector
#   #
#   # Returns:
#   #   A word cloud created from the text vector
#   
#   EnsurePackage("tm")
#   EnsurePackage("wordcloud")
#   EnsurePackage("RColorBrewer")
#   
#   corpus <- tm_map(corpus, function(x) {
#     removeWords(x, c("via", "rt", "mt"))
#   })
#   
#   ap.tdm <- TermDocumentMatrix(corpus)
#   ap.m <- as.matrix(ap.tdm)
#   ap.v <- sort(rowSums(ap.m), decreasing=TRUE)
#   ap.d <- data.frame(word = names(ap.v), freq=ap.v)
#   table(ap.d$freq)
#   pal2 <- brewer.pal(8, "Dark2")
#   
#   wordcloud(ap.d$word, ap.d$freq, 
#             scale=c(8, .2), min.freq = 3, 
#             max.words = Inf, random.order = FALSE, 
#             rot.per = .15, colors = pal2)
# }
# 
# MakeWordCloud(new_corpus)

Mosaic Plot Experiment

  • [ ] TODO: Full timeseries of total eng by brand. (To look for seasonality) - if sports are a driver than seasonality might be important
# p <- unfiltered_ts %>%
#   summarise(jd = doy(timestamp)) %>%
#   group_by(jd) %>%
#   ggplot(aes(factor(jd),total_engagement)) +
#   geom_boxplot() + 
#   facet_grid(~ from_name)
# plot(p)
  • [ ] Populate a table of top performing posts and low performing posts - Tristen can pull shot of tweets for discussion
  • [ ] Create a data.frame with these columns brand, data, tweet, engagement (I think this is a subset of all_companies)

  • [ ] summary table of brand, month, totEng, see examples:http://leonawicz.github.io/HtmlWidgetExamples/ex_dt_sparkline.html

all_companies_ts %>%
  select(from_name, timestamp, total_engagement) %>%
  group_by(from_name, month(timestamp), year(timestamp)) %>%
  summarise(count = n(), 
            engagement = sum(total_engagement)) %>%
  ggplot(., aes(y = log(engagement), x = log(count), colour = from_name)) +
  geom_point() +
  xlab('Post Activity') + ylab('Engagement') +
  geom_smooth(se = FALSE, method = "lm") +
  #geom_smooth(se = FALSE)
  ggtitle("Engagement vs Post Acitivity(Facebook)")

all_companies_ts %>%
  #filter(from_name != "Bud Light" ) %>%
  #filter(from_name != "Michelob ULTRA") %>%
  select(from_name, timestamp, total_engagement) %>%
  group_by(from_name, month(timestamp), year(timestamp)) %>%
  summarise(count = n(),
            engagement = sum(total_engagement)) %>%
  ggplot(., aes(y = log(engagement), x = log(count), colour = from_name)) +
  geom_point() +
  geom_smooth(se = FALSE, method = "lm") +
  ggtitle("Engagement vs Post Acitivity(Facebook)") +
  ylab("Total Engagement") + xlab("Total Monthly Posts")

  • There is a positive relationship between post activity (ie counts) and total engagement.
all_companies_ts %>%
  filter(from_name == "Labatt USA" ) %>%
  select(from_name, timestamp, type, total_engagement) %>%
  group_by(from_name, month(timestamp), year(timestamp), type) %>%
  summarise(count = n(),
            engagement = sum(total_engagement)) %>%
  ggplot(., aes(y = log(engagement), x = log(count), colour = type)) +
  geom_point() +
  geom_smooth(se = FALSE, method = "lm") +
  ggtitle("Post Efficacy by type for Labatt USA") +
  ylab("Total Engagement") + xlab("Total Monthly Posts")

  • [X] TOD vs engagement similar to post activity vs Engagement
all_companies_ts %>%
  filter(from_name == "Labatt USA" ) %>%
  select(from_name, tod, total_engagement) %>%
  ggplot(., aes(y = total_engagement, x = factor(tod), colour = from_name)) +
  geom_boxplot() +
  ylim(c(0,2000)) +
  ggtitle("Post Efficacy by type for Labatt USA(Facebook)") +
  ylab("Total Engagement") + xlab("Time of Day")

Kevins Questions

# load('processed_data/bud_fb.RData')
# bud$total_engagement <- rowSums(bud[,9:11])
# z <- bud %>%
#   arrange(desc(total_engagement))
# head(z)
# Updated upstream

Twitter

text_clean <- function(cleanliness) {
  cleanliness <- str_replace_all(cleanliness, "@\\w+", "")
  cleanliness <- gsub("&amp", "", cleanliness)
  cleanliness <- gsub("(RT|via)((?:\\b\\W*@\\w+)+)", "", cleanliness)
  cleanliness <- gsub("@\\w+", "", cleanliness)
  cleanliness <- gsub("[[:punct:]]", "", cleanliness)
  cleanliness <- gsub("[[:digit:]]", "", cleanliness)
  cleanliness <- gsub("http\\w+", "", cleanliness)
  cleanliness <- gsub("[ \t]{2,}", "", cleanliness)
  cleanliness <- gsub("^\\s+|\\s+$", "", cleanliness)
  return(cleanliness)
}
LabattUSA_timeline$sentiment <- lapply(text_clean(LabattUSA_timeline$text), get_nrc_sentiment)
labatt_sentiment <- data.frame('created' = LabattUSA_timeline$created,
                               'text' = LabattUSA_timeline$text,
                               'sentiment' = as.character(LabattUSA_timeline$sentiment))
labatt_sentiment$score <- get_sentiment(as.character(text_clean(labatt_sentiment$text))) %>% as.numeric()
labatt_sentiment %>%
  ggplot(aes(as_date(created), score)) +
  geom_point() +
  geom_smooth() +
  scale_color_manual(values = colourList) +
  scale_x_date(name = '\nDates', breaks = date_breaks("3 months"), labels = date_format("%Y-%b")) +
  scale_y_continuous(name = "Sentiment Score\n", breaks = seq(-5, 5, by = 1)) + theme_bw() +
  ggtitle('Labatt USA Sentiment(Twitter)')

Molson_Canadian_timeline$sentiment <- lapply(text_clean(Molson_Canadian_timeline$text), get_nrc_sentiment)
molson_sentiment <- data.frame('created' = Molson_Canadian_timeline$created,
                               'text' = Molson_Canadian_timeline$text,
                               'sentiment' = as.character(Molson_Canadian_timeline$sentiment))
molson_sentiment$score <- get_sentiment(as.character(text_clean(molson_sentiment$text))) %>% as.numeric()
molson_sentiment %>%
  ggplot(aes(as_date(created), score)) +
  geom_point() +
  geom_smooth() +
  scale_color_manual(values = colourList) +
  scale_x_date(name = '\nDates', breaks = date_breaks("3 months"), labels = date_format("%Y-%b")) +
  scale_y_continuous(name = "Sentiment Score\n", breaks = seq(-5, 5, by = 1)) + theme_bw() +
  ggtitle('Molson Canadian Sentiment(Twitter)')

budlight_timeline$sentiment <- lapply(text_clean(budlight_timeline$text), get_nrc_sentiment)
budlight_sentiment <- data.frame('created' = budlight_timeline$created,
                               'text' = budlight_timeline$text,
                               'sentiment' = as.character(budlight_timeline$sentiment))
budlight_sentiment$score <- get_sentiment(as.character(text_clean(budlight_sentiment$text))) %>% as.numeric()
budlight_sentiment %>%
  ggplot(aes(as_date(created), score)) +
  geom_point() +
  geom_smooth() +
  scale_color_manual(values = colourList) +
  scale_x_date(name = '\nDates', breaks = date_breaks("3 months"), labels = date_format("%Y-%b")) +
  scale_y_continuous(name = "Sentiment Score\n", breaks = seq(-5, 5, by = 1)) + theme_bw() +
  ggtitle('Bud Light Sentiment(Twitter)')

MichelobULTRA_timeline$sentiment <- lapply(text_clean(MichelobULTRA_timeline$text), get_nrc_sentiment)
michelob_sentiment <- data.frame('created' = MichelobULTRA_timeline$created,
                               'text' = MichelobULTRA_timeline$text,
                               'sentiment' = as.character(MichelobULTRA_timeline$sentiment))
michelob_sentiment$score <- get_sentiment(as.character(text_clean(michelob_sentiment$text))) %>% as.numeric()
michelob_sentiment %>%
  ggplot(aes(as_date(created), score)) +
  geom_point() +
  geom_smooth() +
  scale_color_manual(values = colourList) +
  scale_x_date(name = '\nDates', breaks = date_breaks("3 months"), labels = date_format("%Y-%b")) +
  scale_y_continuous(name = "Sentiment Score\n", breaks = seq(-5, 5, by = 1)) + theme_bw() +
  ggtitle('Michelob ULTRA Sentiment(Twitter)\n')

LS0tCnRpdGxlOiAiTGFiYXR0IFBsYXlib29rIEFuYWx5c2lzIgphdXRob3I6ICJXaWxkRmlnIgpkYXRlOiAnYHIgU3lzLkRhdGUoKWAnCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgd29yZF9kb2N1bWVudDoKICAgIHRvYzogeWVzCnN1YnRpdGxlOiBTb2NpYWwgTWVkaWEgQW5hbHlzaXMgdG8gc3VwcG9ydCBRdWVuY2ggbmV3IGJ1c2luZXNzIHByb3Bvc2FsCi0tLQoKIyMgRXhlY3V0aXZlIFN1bW1hcnkKCmBgYHtyLCBlY2hvPUZBTFNFfQojIFRoaXMgaXMgYSBidWxsZXRlZCBwb2ludCBsaXN0IG9mIHByaW1hcnkgaW5zaWdodHMKYGBgCgotIEluc2lnaHQgMQotIEluc2lnaHQgMgotIEluc2lnaHQgMwoKCiMjIEFic3RyYWN0CgpgYGB7ciwgZWNobz1GQUxTRX0KIyBUaGlzIGlzIGEgdGV4dHVhbCBvdmVydmlldyBvZiBhbGwgdGhlIHdvcmsgYW5kIHRoZSBwcmltYXJ5IGZpbmRpbmdzIHdlIGZvdW5kLiAgVGhpcyBzaG91bGQgYmUgd3JpdHRlbiBmcm9tIHRoZSBjb250ZXh0IHRoYXQgYSBicmFuZCBtYW5hZ2VyIGNvdWxkIHRha2UgdGhlIHRleHQgYW5kIHBsYWNlIGluIGFuIGVtYWlsIHRvIGEgY2xpZW50CmBgYAoKCiMjIEludHJvZHVjdGlvbgoKYGBge3IsIGVjaG89RkFMU0V9CiMgVGhpcyBzZWN0aW9uIGlzIG1lYW50IHRvIGNvbnRhaW4gb3VyIG9iamVjdGl2ZXMgYW5kIGFueSBoeXBvdGhlc2VzIHdlIGFyZSB0ZXN0aW5nIChsYXN0IHBhcmFncmFwaCksIGFzIHdlbGwgYXMgYW55IGluZm9ybWF0aW9uIG9yIHN1bW1hcmllcyBvZiByZXNlYXJjaCBtYXRlcmlhbHMgd2UgdXNlZCBpbiBwcmVwYXJhdGlvbiBmb3Igb3VyIGFuYWx5c2lzIG9yIGZvciBvdXIgaW5zaWdodHMuICAKYGBgCgojIyBNZXRob2RzCgojIyMgRGF0YSBDb2xsZWN0aW9uCkRhdGEgd2FzIGltcG9ydGVkIHVzaW5nIHRoZSBgXGRhdGFfZ2F0aGVyaW5nLlJNRGAgc2NyaXB0LiAgU2VlIHRoYXQgc2NyaXB0IGZvciBkZXRhaWxzIG9mIGNvbGxlY3Rpb24uICAKCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQoKbGlicyA8LSBjKCd0aWR5cicsICdicm9vbScsICdkcGx5cicsICdnZ3Bsb3QyJywgJ2dnZm9ydGlmeScsICd0aWR5dGV4dCcsICdyZWFkcicsICdzdHJpbmdyJywKICAgICAgICAgICdqc29ubGl0ZScsICdwYW5kZXInLCAnUmZhY2Vib29rJywgJ3R3aXR0ZVInLCAnbHVicmlkYXRlJywgJ3NjYWxlcycsICd3b3JkY2xvdWQnLCAnU25vd2JhbGxDJywKICAgICAgICAgICd0bScsICdzeXV6aGV0JywgJ3RpZHlyJywgJ3h0cycpCmxhcHBseShsaWJzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCnJlbW92ZShsaWJzKQoKZmlsZW5hbWVzIDwtIGxpc3QuZmlsZXMocGF0aCA9ICdwcm9jZXNzZWRfZGF0YS8nLCBwYXR0ZXJuID0gJyouUkRhdGEnLCBmdWxsLm5hbWVzID0gVFJVRSkKbGFwcGx5KGZpbGVuYW1lcywgbG9hZCwgLkdsb2JhbEVudikKcmVtb3ZlKGZpbGVuYW1lcykKCmRmX25hbWVzIDwtIGMoJ2xhYmF0dCcsICdtb2xzb24nLCAndWx0cmEnLCAnYnVkJykKY2xpZW50X25hbWVzIDwtIGMoJ0xhYmF0dF9VU0EnLCAnTW9sc29uX0NhbmFkaWFuJywgJ01pY2hlbG9iX1VMVFJBJywgJ2J1ZGxpZ2h0JykKY2xpZW50X25hbWVzX3Byb3BlciA8LSBjKCdMYWJhdHQgVVNBJywgJ01vbHNvbiBDYW5hZGlhbicsICdNaWNoZWxvYiBVTFRSQScsICdCdWQgTGlnaHQnKQpjbGllbnRfaWRzIDwtIGMoMTM0MzkxODQ2NzIzNTQ1LCA0MjQxMDY1NjEwMDQzMDgsIDU3OTIxMzE5ODA4LCA1NDg3NjI0NTA5NCkKCiMgcm1hcmtkb3duOjpyZW5kZXIoJ2JlZXJfYW5hbHlzaXMuUm1kJywgb3V0cHV0X2ZpbGUgPSAnZG9jdW1lbnRzL2JlZXJfYW5hbHlzaXMuaHRtbCcpCmBgYAoKYGBge3IgcGFuZGVyfQpwYW5kZXIodHdpdHRlcl9zdW1tYXJ5X3N0YXRzKQpwYW5kZXIoc3VtbWFyeV9zdGF0cykKYGBgCiMjIyBEYXRhIFNoYXBpbmcKClRha2luZyBpbiByYXcgZGF0YSBhbmQgYWRkaW5nIGEgcGFyc2VhYmxlIHRpbWVzdGFtcCB3aGlsZSBmaWx0ZXJpbmcgb24gdGhlIGRhdGUgYW5kIGNsaWVudF9pZHMuCgpgYGB7ciBkYXRhX3NoYXBpbmcsIGluY2x1ZGUgPSBGQUxTRX0KbGFiYXR0JHRpbWVzdGFtcCA8LSB5bWRfaG1zKGxhYmF0dCRjcmVhdGVkX3RpbWUpCmxhYmF0dCR0aW1lc3RhbXAgPC0gd2l0aF90eihsYWJhdHQkdGltZXN0YW1wLCAiQW1lcmljYS9OZXdfWW9yayIpCgptb2xzb24kdGltZXN0YW1wIDwtIHltZF9obXMobW9sc29uJGNyZWF0ZWRfdGltZSkKbW9sc29uJHRpbWVzdGFtcCA8LSB3aXRoX3R6KG1vbHNvbiR0aW1lc3RhbXAsICJBbWVyaWNhL05ld19Zb3JrIikKCnVsdHJhJHRpbWVzdGFtcCA8LSB5bWRfaG1zKHVsdHJhJGNyZWF0ZWRfdGltZSkKdWx0cmEkdGltZXN0YW1wIDwtIHdpdGhfdHoodWx0cmEkdGltZXN0YW1wLCAiQW1lcmljYS9OZXdfWW9yayIpCgpidWQkdGltZXN0YW1wIDwtIHltZF9obXMoYnVkJGNyZWF0ZWRfdGltZSkKYnVkJHRpbWVzdGFtcCA8LSB3aXRoX3R6KGJ1ZCR0aW1lc3RhbXAsICJBbWVyaWNhL05ld19Zb3JrIikKCmFsbF9jb21wYW5pZXNfdHMgPC0gcmJpbmQobGFiYXR0LCBtb2xzb24sIHVsdHJhLCBidWQpCmFsbF9jb21wYW5pZXNfdHMgPC0gc3Vic2V0KGFsbF9jb21wYW5pZXNfdHMsIHNlbGVjdCA9IC1jKG1lc3NhZ2UsIGNyZWF0ZWRfdGltZSwgbGluaywgaWQpKQphbGxfY29tcGFuaWVzX3RzJHRvdGFsX2VuZ2FnZW1lbnQgPC0gcm93U3VtcyhhbGxfY29tcGFuaWVzX3RzWzU6N10pCgphbGxfY29tcGFuaWVzX3RzIDwtIGFsbF9jb21wYW5pZXNfdHMgJT4lCiAgZmlsdGVyKGZyb21faWQgJWluJSBjbGllbnRfaWRzKQoKbGFiYXR0IDwtIGxhYmF0dCAlPiUKICBmaWx0ZXIoZnJvbV9pZCAlaW4lIGNsaWVudF9pZHMpCgptb2xzb24gPC0gbW9sc29uICU+JQogIGZpbHRlcihmcm9tX2lkICVpbiUgY2xpZW50X2lkcykKCnVsdHJhIDwtIHVsdHJhICU+JQogIGZpbHRlcihmcm9tX2lkICVpbiUgY2xpZW50X2lkcykKCmJ1ZCA8LSBidWQgJT4lCiAgZmlsdGVyKGZyb21faWQgJWluJSBjbGllbnRfaWRzKQoKIyBCdWlsZCBTdW1tYXJ5IFN0YXRzIERhdGFGcmFtZQpDb21wYW55IDwtIGMoJ0xhYmF0dCBVU0EnLCAnTW9sc29uIENhbmFkaWFuJywgJ01pY2hlbG9iIFVMVFJBJywgJ0J1ZCBMaWdodCcpCkNvbW1lbnRzIDwtIGMoc3VtKGxhYmF0dCRjb21tZW50c19jb3VudCksIHN1bShtb2xzb24kY29tbWVudHNfY291bnQpLCBzdW0odWx0cmEkY29tbWVudHNfY291bnQpLCBzdW0oYnVkJGNvbW1lbnRzX2NvdW50KSkKTGlrZXMgPC0gYyhzdW0obGFiYXR0JGxpa2VzX2NvdW50KSwgc3VtKG1vbHNvbiRsaWtlc19jb3VudCksIHN1bSh1bHRyYSRsaWtlc19jb3VudCksIHN1bShidWQkbGlrZXNfY291bnQpKQpTaGFyZXMgPC0gYyhzdW0obGFiYXR0JHNoYXJlc19jb3VudCksIHN1bShtb2xzb24kc2hhcmVzX2NvdW50KSwgc3VtKHVsdHJhJHNoYXJlc19jb3VudCksIHN1bShidWQkc2hhcmVzX2NvdW50KSkKVG90YWwuUG9zdHMgPC0gYyhucm93KGxhYmF0dCksIG5yb3cobW9sc29uKSwgbnJvdyh1bHRyYSksIG5yb3coYnVkKSkKc3VtbWFyeV9zdGF0cyA8LSBkYXRhLmZyYW1lKENvbXBhbnksIENvbW1lbnRzLCBMaWtlcywgU2hhcmVzLCBUb3RhbC5Qb3N0cykKYGBgCgoKIyMjIEZ1bmN0aW9uIERlZmluaXRpb24KCgpEZWZpbmUgZnVuY3Rpb25zIHRvIGNyZWF0ZSBwb3N0cyBwZXIgZGF5IG9mIHdlZWsgZ3JhcGhzLCBhbmQgdGltZXNlcmllcyBvZiBlbmdhZ2VtZW50IGxpbmUgZ3JhcGhzLgoKYGBge3IgZnVuY3Rpb25zLCBpbmNsdWRlID0gRkFMU0V9CmRheV9vZl93ZWVrIDwtIGZ1bmN0aW9uKGRmLCBjbGllbnQpIHsKICByIDwtIGdldChkZikgJT4lCiAgICBmaWx0ZXIoZnJvbV9pZCAlaW4lIGNsaWVudF9pZHMpCiAgZ2dwbG90KGRhdGEgPSByLCBhZXMoeCA9IHdkYXkodGltZXN0YW1wLCBsYWJlbCA9IFRSVUUpKSkgKwogICAgZ2VvbV9iYXIoYWVzKGZpbGwgPSAuLmNvdW50Li4pKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgZXhwYW5kX2xpbWl0cyh5PWMoMCwxMDApKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKDAsIDEwMCwgMjAwLCAzMDAsIDQwMCkpICsKICAgIHhsYWIoIkRheSBvZiB0aGUgV2VlayIpICsgeWxhYigiTnVtYmVyIG9mIFBvc3RzIikgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibWlkbmlnaHRibHVlIiwgaGlnaCA9ICJhcXVhbWFyaW5lNCIpCn0KCnRpbWVzZXJpZXNfZW5nYWdlbWVudCA8LSBmdW5jdGlvbihjbGllbnQpIHsKICByIDwtIGFsbF9jb21wYW5pZXNfdHMgCiAgciA8LSBmaWx0ZXIociwgZnJvbV9uYW1lID09IGNsaWVudCkKICBnZ3Bsb3QoZGF0YSA9IHIsIGFlcyh4ID0gdGltZXN0YW1wLCB5ID0gdG90YWxfZW5nYWdlbWVudCkpICsgCiAgICBnZW9tX2xpbmUoc2l6ZSA9IDEpCn0KYGBgCgoKIyMjIEFkZGl0aW9uYWwgRGF0YSBTaGFwaW5nIGZvciBFbmdhZ2VtZW50CgpTaGFwZSBkYXRhIGludG8gdmVydGljYWwgZGF0YSBmb3JtYXRzLgoKYGBge3IgYWRkdGxfZGF0YV9zaGFwaW5nLCBpbmNsdWRlID0gRkFMU0V9CiMgQ3JlYXRlIFZlcnRpY2FsIHN1bW1hcnlfc3RhdHMgZm9yIExhYmF0dApzdW1tYXJ5X3N0YXRzIDwtIGdhdGhlcihzdW1tYXJ5X3N0YXRzLCBFbmdhZ2VtZW50LCBOdW1iZXIsIENvbW1lbnRzOlRvdGFsLlBvc3RzKQpsYWJhdHRfZW5nYWdlbWVudCA8LSBsYWJhdHQgJT4lCiAgc2VsZWN0KGZyb21fbmFtZSwgdHlwZSwgbGlrZXNfY291bnQsIGNvbW1lbnRzX2NvdW50LCBzaGFyZXNfY291bnQpICU+JQogIGdhdGhlcihjb3VudF9uYW1lLCB2YWx1ZSwgbGlrZXNfY291bnQ6c2hhcmVzX2NvdW50KQoKYWxsX2VuZ2FnZW1lbnQgPC0gYWxsX2NvbXBhbmllc190cyAlPiUKICBzZWxlY3QoZnJvbV9uYW1lLCB0eXBlLCBsaWtlc19jb3VudCwgY29tbWVudHNfY291bnQsIHNoYXJlc19jb3VudCkgJT4lCiAgZ2F0aGVyKGNvdW50X25hbWUsIHZhbHVlLCBsaWtlc19jb3VudDpzaGFyZXNfY291bnQpCgojIFBsb3RzIGZvciBFbmdhZ2VtZW50IGJ5IFR5cGUgZm9yIExhYmF0dApsYWJhdHRfZW5nYWdlbWVudCR0eXBlIDwtIGFzLmNoYXJhY3RlcihsYWJhdHRfZW5nYWdlbWVudCR0eXBlKQpsYWJhdHRfZW5nYWdlbWVudCR0eXBlW2xhYmF0dF9lbmdhZ2VtZW50JHR5cGUgPT0gInBob3RvIl0gPC0gIlBob3RvIgoKbGFiYXR0X2VuZ2FnZW1lbnQkdHlwZSA8LSBhcy5jaGFyYWN0ZXIobGFiYXR0X2VuZ2FnZW1lbnQkdHlwZSkKbGFiYXR0X2VuZ2FnZW1lbnQkdHlwZVtsYWJhdHRfZW5nYWdlbWVudCR0eXBlID09ICJ2aWRlbyJdIDwtICJWaWRlbyIKCmxhYmF0dF9lbmdhZ2VtZW50JHR5cGUgPC0gYXMuY2hhcmFjdGVyKGxhYmF0dF9lbmdhZ2VtZW50JHR5cGUpCmxhYmF0dF9lbmdhZ2VtZW50JHR5cGVbbGFiYXR0X2VuZ2FnZW1lbnQkdHlwZSA9PSAibGluayJdIDwtICJMaW5rIgoKbGFiYXR0X2VuZ2FnZW1lbnQkdHlwZSA8LSBhcy5jaGFyYWN0ZXIobGFiYXR0X2VuZ2FnZW1lbnQkdHlwZSkKbGFiYXR0X2VuZ2FnZW1lbnQkdHlwZVtsYWJhdHRfZW5nYWdlbWVudCR0eXBlID09ICJzdGF0dXMiXSA8LSAiU3RhdHVzIgoKbGFiYXR0X2VuZ2FnZW1lbnQkdHlwZSA8LSBhcy5jaGFyYWN0ZXIobGFiYXR0X2VuZ2FnZW1lbnQkdHlwZSkKbGFiYXR0X2VuZ2FnZW1lbnQkdHlwZVtsYWJhdHRfZW5nYWdlbWVudCR0eXBlID09ICJtdXNpYyJdIDwtICJNdXNpYyIKCmxhYmF0dF9lbmdhZ2VtZW50JHR5cGUgPC0gYXMuY2hhcmFjdGVyKGxhYmF0dF9lbmdhZ2VtZW50JHR5cGUpCmxhYmF0dF9lbmdhZ2VtZW50JHR5cGVbbGFiYXR0X2VuZ2FnZW1lbnQkdHlwZSA9PSAiZXZlbnQiXSA8LSAiRXZlbnQiCgpsYWJhdHRfZW5nYWdlbWVudCRjb3VudF9uYW1lIDwtIGFzLmNoYXJhY3RlcihsYWJhdHRfZW5nYWdlbWVudCRjb3VudF9uYW1lKQpsYWJhdHRfZW5nYWdlbWVudCRjb3VudF9uYW1lW2xhYmF0dF9lbmdhZ2VtZW50JGNvdW50X25hbWUgPT0gImxpa2VzX2NvdW50Il0gPC0gIkxpa2VzIgoKbGFiYXR0X2VuZ2FnZW1lbnQkY291bnRfbmFtZSA8LSBhcy5jaGFyYWN0ZXIobGFiYXR0X2VuZ2FnZW1lbnQkY291bnRfbmFtZSkKbGFiYXR0X2VuZ2FnZW1lbnQkY291bnRfbmFtZVtsYWJhdHRfZW5nYWdlbWVudCRjb3VudF9uYW1lID09ICJzaGFyZXNfY291bnQiXSA8LSAiU2hhcmVzIgoKbGFiYXR0X2VuZ2FnZW1lbnQkY291bnRfbmFtZSA8LSBhcy5jaGFyYWN0ZXIobGFiYXR0X2VuZ2FnZW1lbnQkY291bnRfbmFtZSkKbGFiYXR0X2VuZ2FnZW1lbnQkY291bnRfbmFtZVtsYWJhdHRfZW5nYWdlbWVudCRjb3VudF9uYW1lID09ICJjb21tZW50c19jb3VudCJdIDwtICJDb21tZW50cyIKCiMgQWxsIEVuZ2FnZW1lbnQKYWxsX2VuZ2FnZW1lbnQkdHlwZSA8LSBhcy5jaGFyYWN0ZXIoYWxsX2VuZ2FnZW1lbnQkdHlwZSkKYWxsX2VuZ2FnZW1lbnQkdHlwZVthbGxfZW5nYWdlbWVudCR0eXBlID09ICJwaG90byJdIDwtICJQaG90byIKCmFsbF9lbmdhZ2VtZW50JHR5cGUgPC0gYXMuY2hhcmFjdGVyKGFsbF9lbmdhZ2VtZW50JHR5cGUpCmFsbF9lbmdhZ2VtZW50JHR5cGVbYWxsX2VuZ2FnZW1lbnQkdHlwZSA9PSAidmlkZW8iXSA8LSAiVmlkZW8iCgphbGxfZW5nYWdlbWVudCR0eXBlIDwtIGFzLmNoYXJhY3RlcihhbGxfZW5nYWdlbWVudCR0eXBlKQphbGxfZW5nYWdlbWVudCR0eXBlW2FsbF9lbmdhZ2VtZW50JHR5cGUgPT0gImxpbmsiXSA8LSAiTGluayIKCmFsbF9lbmdhZ2VtZW50JHR5cGUgPC0gYXMuY2hhcmFjdGVyKGFsbF9lbmdhZ2VtZW50JHR5cGUpCmFsbF9lbmdhZ2VtZW50JHR5cGVbYWxsX2VuZ2FnZW1lbnQkdHlwZSA9PSAic3RhdHVzIl0gPC0gIlN0YXR1cyIKCmFsbF9lbmdhZ2VtZW50JHR5cGUgPC0gYXMuY2hhcmFjdGVyKGFsbF9lbmdhZ2VtZW50JHR5cGUpCmFsbF9lbmdhZ2VtZW50JHR5cGVbYWxsX2VuZ2FnZW1lbnQkdHlwZSA9PSAibXVzaWMiXSA8LSAiTXVzaWMiCgphbGxfZW5nYWdlbWVudCR0eXBlIDwtIGFzLmNoYXJhY3RlcihhbGxfZW5nYWdlbWVudCR0eXBlKQphbGxfZW5nYWdlbWVudCR0eXBlW2FsbF9lbmdhZ2VtZW50JHR5cGUgPT0gImV2ZW50Il0gPC0gIkV2ZW50IgoKYWxsX2VuZ2FnZW1lbnQkY291bnRfbmFtZSA8LSBhcy5jaGFyYWN0ZXIoYWxsX2VuZ2FnZW1lbnQkY291bnRfbmFtZSkKYWxsX2VuZ2FnZW1lbnQkY291bnRfbmFtZVthbGxfZW5nYWdlbWVudCRjb3VudF9uYW1lID09ICJsaWtlc19jb3VudCJdIDwtICJMaWtlcyIKCmFsbF9lbmdhZ2VtZW50JGNvdW50X25hbWUgPC0gYXMuY2hhcmFjdGVyKGFsbF9lbmdhZ2VtZW50JGNvdW50X25hbWUpCmFsbF9lbmdhZ2VtZW50JGNvdW50X25hbWVbYWxsX2VuZ2FnZW1lbnQkY291bnRfbmFtZSA9PSAic2hhcmVzX2NvdW50Il0gPC0gIlNoYXJlcyIKCmFsbF9lbmdhZ2VtZW50JGNvdW50X25hbWUgPC0gYXMuY2hhcmFjdGVyKGFsbF9lbmdhZ2VtZW50JGNvdW50X25hbWUpCmFsbF9lbmdhZ2VtZW50JGNvdW50X25hbWVbYWxsX2VuZ2FnZW1lbnQkY291bnRfbmFtZSA9PSAiY29tbWVudHNfY291bnQiXSA8LSAiQ29tbWVudHMiCgpgYGAKCmBgYHtyIEZlYXR1cmVFbmcsIGVjaG89RkFMU0V9CiMgQ3JlYXRlIGEgZGF5IG9mIHdlZWsgdmVjdG9yCmFsbF9jb21wYW5pZXNfdHMkZG93IDwtIHdkYXkoYWxsX2NvbXBhbmllc190cyR0aW1lc3RhbXAsIGxhYmVsPVRSVUUpCgojIENyZWF0ZSBhIHRpbWUgb2YgZGF5IHZlY3RvcgpsaWJyYXJ5KGNocm9uKQphbGxfY29tcGFuaWVzX3RzJHRvZCA8LSBob3VycyhhbGxfY29tcGFuaWVzX3RzJHRpbWVzdGFtcCkKYGBgCgojIyBSZXN1bHRzCgojIyMgU3VtbWFyeSBTdGF0aXN0aWNzCgotIExldHMgc3RhcnQgaGVyZSB3aXRoIGEgdGFibGUgb2Ygc3VtbWFyeSBzdGF0aXN0aWNzCmBgYHtyIHN1bVRhYmxlUHJlcCwgZWNobz1GQUxTRX0KZGF0IDwtIGFzX2RhdGFfZnJhbWUoYWxsX2NvbXBhbmllc190cykKY2xhc3MoZGF0KQpgYGAKCiMjIyBNYXRyaWNlcyBwbG90cyBvZiBFbmdhZ2VtZW50CgpGaXJzdCBwbG90IGlzIGFnZ3JlZ2F0ZWQgZW5nYWdlbWVudCBieSBjb250ZW50IHR5cGUuIFNlY29uZCBwbG90LCBpdCBlbmdhZ2VtZW50IGJ5IHR5cGUgZm9yIGNsaWVudChMYWJhdHQpLgoKYGBge3IgbWF0cml4X2VuZ2FnZW1lbnQsIGluY2x1ZGUgPSBUUlVFLCBlY2hvID0gRkFMU0V9CnAgPC0gYWxsX2VuZ2FnZW1lbnQgJT4lCiAgZmlsdGVyKHR5cGUgIT0gIk11c2ljIikgJT4lCiAgZ2dwbG90KC4sIGFlcyh4ID0gdHlwZSwgeSA9IGNvdW50X25hbWUpKSArIAogICBmYWNldF9ncmlkKH5mcm9tX25hbWUpICsKICAgc3RhdF9zdW0oYWVzKGdyb3VwID0gdmFsdWUsIGNvbG9yID0gdHlwZSkpICsgCiAgIHNjYWxlX3NpemUocmFuZ2UgPSBjKDUsIDE1KSkgKwogICB4bGFiKCJQb3N0IENvbnRlbnQgVHlwZSIpICsgCiAgIHlsYWIoIkVuZ2FnZW1lbnQgVHlwZSIpICsKICAgY29vcmRfZmxpcCgpICsgCiAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsKICAgZ2d0aXRsZSgiQWdncmVnYXRlZCBFbmdhZ2VtZW50IGJ5IENvbnRlbnQgVHlwZShGYWNlYm9vaykiKQoKcGxvdChwKQpgYGAKCi0gQXMgKkJ1ZCBMaWdodCogYW5kICpNaWNoZWxvYiBVTFRSQSogYXJlIHRoZSB0byBjb21wYW5pZXMgd2l0aCB0aGUgaGlnaGVzdCBlbmdhZ2VtZW50LCBjb21wYXJpc29uIG9mIAoKYGBge3IgbWF0cml4X2VuZ2FnZW1lbnQyLCBpbmNsdWRlID0gVFJVRSwgZWNobyA9IEZBTFNFfQpwIDwtIGxhYmF0dF9lbmdhZ2VtZW50ICU+JQogIGZpbHRlcih0eXBlICE9ICJNdXNpYyIpICU+JQogIGZpbHRlcihmcm9tX25hbWUgPT0gIkxhYmF0dCBVU0EiKSAlPiUKICBnZ3Bsb3QoLiwgYWVzKHggPSB0eXBlLCB5ID0gY291bnRfbmFtZSkpICsgCiAgc3RhdF9zdW0oYWVzKGdyb3VwID0gdmFsdWUsIGNvbG9yID0gdHlwZSkpICsgc2NhbGVfc2l6ZShyYW5nZSA9IGMoNSwgMTUpKSArCiAgeGxhYigiUG9zdCBDb250ZW50IFR5cGUiKSArIHlsYWIoIkVuZ2FnZW1lbnQgVHlwZSIpICsgCiAgY29vcmRfZmxpcCgpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKwogIGdndGl0bGUoIkxhYmF0dCBBZ2cuIEVuZ2FnZW1lbnQgYnkgQ29udGVudCBUeXBlKEZhY2Vib29rKSIpCnBsb3QocCkKYGBgCgotIExvb2tpbmcgYXQgdGhlIGVuZ2FnZW1lbnQgYnkgY29udGVudCB0eXBlIHdlIHNlZSB0aGF0ICoqTGFiYXR0KiogaXMgZ2FybmVyaW5nIGl0cyBtb3N0IHNpZ25pZmljYW50IGVuZ2FnbWVudCBvbiBQaG90b3MsIFZpZGVvLCBhbmQgTGlua3MuICAKCgoKLSBbIF0gVE9ETzogd2UgbmVlZCB0byBjb21wYXJlIHBvc3RpbmcgYWN0aXZpdHkgd2l0aCBlbmdhZ2VtZW50IGFjdGl2aXR5IChzY2F0dGVyIHBsb3QpCgojIyMgU3VtbWFyeSBQbG90cwoKSG9yaXpvbnRhbCBzdGFja2VkIGJhciBjaGFydCBmb3IgdG90YWwgZW5nYWdlbWVudCBjb21wYXJpc29uIG9mIGFsbCBjb21wYW5pZXMKCmBgYHtyIHN1bW1hcnlfcGxvdHMsIGluY2x1ZGUgPSBUUlVFfQpyZW9yZGVyX3NpemUgPC0gZnVuY3Rpb24oeCkgewogIGZhY3Rvcih4LCBsZXZlbHMgPSBuYW1lcyhzb3J0KHRhYmxlKHgpKSkpCn0KcCA8LSBzdW1tYXJ5X3N0YXRzICU+JQogIGZpbHRlcihFbmdhZ2VtZW50ICE9ICJUb3RhbC5Qb3N0cyIpICU+JQogIGdncGxvdCguLCBhZXMoeCA9IENvbXBhbnksIHkgPSBsb2coTnVtYmVyKSwgZmlsbCA9IEVuZ2FnZW1lbnQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICB4bGFiKCdCcmFuZCcpICsgeWxhYignRW5nYWdlbWVudChTY2FsZWQpJykgKwogIGdndGl0bGUoJ0xvZ2FyaXRobWljIFRyYW5zZm9ybWF0aW9uIG9mIFRvdGFsIEVuZ2FnZW1lbnQoRmFjZWJvb2spJykgKwogIGNvb3JkX2ZsaXAoKQoKcGxvdChwKQpgYGAKCiMjIyBEYXkgb2YgV2VlawoKVG90YWwgcG9zdHMgcGVyIGRheSBvZiB0aGUgd2Vlay4KYGBge3IgZGF5X29mX3dlZWssIGluY2x1ZGUgPSBUUlVFfQojIHdpdGhvdXQgYnJhbmQgSUQgdGhlc2UgYXJlIHVuaW5mb3JtYXRpdmUKZm9yKGkgaW4gc2VxX2Fsb25nKGRmX25hbWVzKSkgewogIHAgPC0gZGF5X29mX3dlZWsoZGZfbmFtZXNbaV0sIGNsaWVudF9uYW1lc1tpXSkKICBwbG90KHApCn0KYGBgCgoKYGBge3IgUG9zdHNfZGF5X29mX3dlZWssIGluY2x1ZGUgPSBUUlVFfQpwIDwtIGdncGxvdChkYXRhID0gYWxsX2NvbXBhbmllc190cywgYWVzKHggPSB3ZGF5KHRpbWVzdGFtcCwgbGFiZWwgPSBUUlVFKSkpICsKICBnZW9tX2JhcihhZXMoZmlsbCA9IC4uY291bnQuLikpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICB4bGFiKCJEYXkgb2YgdGhlIFdlZWsiKSArIHlsYWIoIk51bWJlciBvZiBQb3N0cyIpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJtaWRuaWdodGJsdWUiLCBoaWdoID0gImFxdWFtYXJpbmU0IikgKyAKICBmYWNldF93cmFwKH5mcm9tX25hbWUsIG5jb2wgPSA0KSArCiAgZ2d0aXRsZSgiRGFpbHkgUG9zdGluZyBBY3Rpdml0eSBieSBCcmFuZChGYWNlYm9vaykiKQpwbG90KHApCmBgYAoKCi0gV2hhdCBpcyB0aGUgdG90YWwgbnVtYmVyIG9mIHBvc3RzPyAgCmBgYHtyIEVuZ19kYXlfb2Zfd2VlaywgaW5jbHVkZSA9IFRSVUV9CmRvd0RhdCA8LSBzZWxlY3QoYWxsX2NvbXBhbmllc190cywgdG90YWxfZW5nYWdlbWVudCxmcm9tX25hbWUsIHRpbWVzdGFtcCkKZG93RGF0JGRvdyA8LSB3ZGF5KGRvd0RhdCR0aW1lc3RhbXAsIGxhYmVsPVRSVUUpCmRvd0RhdCA8LSBhZ2dyZWdhdGUodG90YWxfZW5nYWdlbWVudH5kb3crZnJvbV9uYW1lLCBkYXRhPWRvd0RhdCwgRlVOPW1lYW4pCgpwIDwtIGdncGxvdChkb3dEYXQsIGFlcyh4ID0gZG93LCB5ID0gdG90YWxfZW5nYWdlbWVudCkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGFlcyhmaWxsID0gdG90YWxfZW5nYWdlbWVudCkpICsgCiAgZmFjZXRfZ3JpZCh+ZnJvbV9uYW1lKSArIAogIGdndGl0bGUoJ0VuZ2FnZW1lbnRzIFBlciBEYXkgb2YgV2VlayhGYWNlYm9vayknKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgeGxhYigiRGF5IG9mIHRoZSBXZWVrIikgKyB5bGFiKCJOdW1iZXIgb2YgRW5nYWdlbWVudHMiKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibWlkbmlnaHRibHVlIiwgaGlnaCA9ICJhcXVhbWFyaW5lNCIpCnBsb3QocCkKYGBgCgotWyBdIFRPRE86IENyZWF0ZSBhIHBsb3QgZm9yIFBvc3QgYnkgZW5nYWdlbWVudCBncmFwaGljcyAoc2NhdHRlciBwbG90KS4gIFRvIGFuc3dlciB0aGUgcXVlc3Rpb24gb24gZGF5cyB3aXRoIGxvdHMgb2YgcG9zdHMgZG8gd2UgZ2V0IGxvdHMgb2YgZW5nYWdtZW50LiAKCmBgYHtyfQptZGF0IDwtIGFsbF9jb21wYW5pZXNfdHMKbWRhdCRtb250aCA8LSBmb3JtYXQoYXMuUE9TSVhjdChtZGF0JHRpbWVzdGFtcCksICclbScpCm1kYXQgJT4lCiAgZ2dwbG90KGFlcyhtb250aCwgbG9nKHRvdGFsX2VuZ2FnZW1lbnQpKSkgKwogIGdlb21fYm94cGxvdCgpICsKICBnZ3RpdGxlKCdFbmdhZ21lbnQgZ3JvdXBlZCBieSBNb250aChGYWNlYm9vayknKSArIHlsYWIoJ0VuZ2FnZW1lbnQnKSArIHhsYWIoJ01vbnRoJykgKwogIGZhY2V0X2dyaWQoZnJvbV9uYW1lIH4gLiwgc2NhbGVzID0gImZyZWUiKQpgYGAKCgoKCi0gW10gVE9ETzogV2l0aCB0aGF0IGRhdGEgd2UgY2FuIGFzayB3aGF0IHBvc3RzIGdldCB0aGUgbW9zdCBlbmdhZ21lbnQsIHdlIGNhbiBsb29rIGF0IHRvcCBlbmdhZ21lbnQgYW5kIGJvdHRvbSBlbmdhZ2VtZW50cyBwb3N0cyBhbmQgd2hhdCBxdWFsaXRpZXMgdGhleSBzaGFyZSBvciBkaWZmZXIgYnkuICAKCiMjIyBFbmdhZ2VtZW50IGJ5IFRpbWUgb2YgRGF5IChUT0QpCgpgYGB7ciBFbmdUb2QsIGVjaG89RkFMU0V9CmFsbF9jb21wYW5pZXNfdHMgJT4lCiAgc2VsZWN0KGZyb21fbmFtZSwgdG90YWxfZW5nYWdlbWVudCwgdG9kKSAlPiUKICBnZ3Bsb3QoLiwgYWVzKHk9dG90YWxfZW5nYWdlbWVudCwgeCA9IGZhY3Rvcih0b2QpKSkgKwogIGdlb21fYm94cGxvdCgpICsKICBmYWNldF93cmFwKH5mcm9tX25hbWUpICsKICBnZ3RpdGxlKCJGYWNlYm9vayBCcmFuZCBFbmdhZ2VtZW50IGJ5IHRpbWUgb2YgZGF5IikgKwogIHlsYWIoIlRvdGFsIEVuZ2FnbWVudCIpICsgeGxhYigiVGltZSBvZiBEYXkiKQpgYGAKCmBgYHtyIEVuZ1RvZE5vQnVkb3JVbHRyYSwgZWNobz1GQUxTRX0KYWxsX2NvbXBhbmllc190cyAlPiUKICBmaWx0ZXIoZnJvbV9uYW1lICE9ICJCdWQgTGlnaHQiICkgJT4lCiAgZmlsdGVyKGZyb21fbmFtZSAhPSAiTWljaGVsb2IgVUxUUkEiKSAlPiUKICBzZWxlY3QoZnJvbV9uYW1lLCB0b3RhbF9lbmdhZ2VtZW50LCB0b2QpICU+JQogIGdncGxvdCguLCBhZXMoeT10b3RhbF9lbmdhZ2VtZW50LCB4ID0gZmFjdG9yKHRvZCkpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGZhY2V0X3dyYXAofmZyb21fbmFtZSkgKwogIGdndGl0bGUoIkZhY2Vib29rIEJyYW5kIEVuZ2FnZW1lbnQgYnkgdGltZSBvZiBkYXkgKHcvbyBCdWQgYW5kIE1pY2ggVUxUUkEpIikgKwogIHlsYWIoIlRvdGFsIEVuZ2FnbWVudCIpICsgeGxhYigiVGltZSBvZiBEYXkiKQpgYGAKCiMjIyBUaW1lc2VyaWVzIEVuZ2FnZW1lbnQgClBsb3RzIGZvciB0aGUgdGltZXNlcmllcyBlbmdhZ2VtZW50IGxpbmUuCmBgYHtyIGxpbmVfdGltZXNlcmllc19lbmdhZ2VtZW50LCBpbmNsdWRlID0gVFJVRX0KZm9yKGkgaW4gc2VxX2Fsb25nKGRmX25hbWVzKSkgewogIHAgPC0gdGltZXNlcmllc19lbmdhZ2VtZW50KGNsaWVudF9uYW1lc19wcm9wZXJbaV0pCiAgcGxvdChwKQp9CmBgYAoKIyMjIEluaXRpYWwgVmlzdWFsaXphdGlvbiBvZiBlbmdhZ2VtZW50IG92ZXIgdGltZSBvbiBhIGxpbmUKCmBgYHtyIHRlc3Rfdml6LCBpbmNsdWRlID0gVFJVRX0KYWxsX2NvbXBhbmllc190cyA8LSBhbGxfY29tcGFuaWVzX3RzICU+JQogIGZpbHRlcihmcm9tX2lkICVpbiUgY2xpZW50X2lkcykgJT4lCiAgbXV0YXRlKG1vbnRoID0gYXMuRGF0ZShjdXQoYWxsX2NvbXBhbmllc190cyR0aW1lc3RhbXAsIGJyZWFrcyA9ICJtb250aCIpKSkKCmFsbF9jb21wYW5pZXNfdHMgJT4lCiAgc2VsZWN0KGZyb21fbmFtZSwgbW9udGgsIHRvdGFsX2VuZ2FnZW1lbnQpICU+JQogIGdyb3VwX2J5KGZyb21fbmFtZSxtb250aCkgJT4lCiAgc3VtbWFyaXNlKHRvdEVuZyA9IHN1bSh0b3RhbF9lbmdhZ2VtZW50KSkgJT4lCiAgZ2dwbG90KC4sIGFlcyh4ID0gbW9udGgsIHkgPSB0b3RFbmcpKSArCiAgeWxhYignVG90YWwgRW5nYWdlbWVudHMnKSArIHhsYWIoJ1llYXJzJykgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZnJvbV9uYW1lKSkgKyB5bGltKDAsIDIyMDAwMDApICsKICBnZ3RpdGxlKCdFbmdhZ2VtZW50IE92ZXIgVGltZShGYWNlYm9vayknKSArCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbG9yID0gZnJvbV9uYW1lKSwgc2UgPSBGQUxTRSkKYGBgCgoKCmBgYHtyfQphbGxfY29tcGFuaWVzX3RzICU+JQogIHNlbGVjdChmcm9tX25hbWUsIG1vbnRoLCB0b3RhbF9lbmdhZ2VtZW50LCB0aW1lc3RhbXApICU+JQogIGZpbHRlcihmcm9tX25hbWUgIT0gIkJ1ZCBMaWdodCIgKSAlPiUKICBmaWx0ZXIoZnJvbV9uYW1lICE9ICJNaWNoZWxvYiBVTFRSQSIpICU+JQogIGZpbHRlcih5ZWFyKHRpbWVzdGFtcCkgJWluJSBjKCcyMDE1JywgJzIwMTYnKSkgJT4lCiAgZ3JvdXBfYnkoZnJvbV9uYW1lLG1vbnRoKSAlPiUKICBzdW1tYXJpc2UodG90RW5nID0gc3VtKHRvdGFsX2VuZ2FnZW1lbnQpKSAlPiUKICBnZ3Bsb3QoLiwgYWVzKHggPSBtb250aCwgeSA9IHRvdEVuZykpICsKICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBmcm9tX25hbWUpKSArCiAgIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IGZyb21fbmFtZSksIHNlID0gRkFMU0UpICsKICAgZ2d0aXRsZSgiTW9udGhseSBGYWNlYm9vayBFbmdhZ2VtZW50IExhYmF0dCB2cyBNb2xzb24iKQpgYGAKCi0gVGhpcyBpcyBhbiBpbnRlcmVzdGluZyBkcm9wIG9mIH4zMCUgb3ZlciB0aGUgZmlyc3QgNiBtb250aHMgb2YgMjAxNS4gIFRoZSBicmFuZCBoYXMgc3RpbGwgbm90IHJlY292ZXJlZCBmcm9tIHRoYXQgcmVkdWN0aW9uLiAgCiAgICsgV2hhdCBpcyBkaWZmZXJlbnQgYWJvdXQgdGhlIGNvbnRlbnQgZHVyaW5nIHRoaXMgcGVyaW9kPwogICAKLSBNaWdodCBiZSB2YWx1YWJsZSB0byBsb29rIGJhY2sgYXQgdGhlIGVudGlyZSB0aW1lc2VyaWVzIGZvciBwZXJpb2RzIG9mICBkaXN0aW5jdCBkeW5hbWlzbS4KICAgCgojIyMgTGFiYXR0IFdvcmRjbG91ZHMKUmVtb3ZlZCBmaWx0ZXIgYmVjYXVzZSBsYWJhdHQgZG9lcyBub3QgaGF2ZSBzaWduaWZpY2FudCBpbmZsZWN0aW9uIHBvaW50IHdoZXJlYXMgcHJldmlvdXMgYW5hbHlzaXMKYGBge3IgbGFiYXR0X3dvcmRjbG91ZHMsIGluY2x1ZGUgPSBUUlVFfQpsYWJhdHQkdGltZXN0YW1wIDwtIGRhdGUobGFiYXR0JHRpbWVzdGFtcCkKCmxhYmF0dF9jbGVhbl9wcmUgPC0gc3RyX3JlcGxhY2VfYWxsKGxhYmF0dCRtZXNzYWdlLCAiQFxcdysiLCAiIikKbGFiYXR0X2NsZWFuX3ByZSA8LSBnc3ViKCImYW1wIiwgIiIsIGxhYmF0dF9jbGVhbl9wcmUpCmxhYmF0dF9jbGVhbl9wcmUgPC0gZ3N1YigiKFJUfHZpYSkoKD86XFxiXFxXKkBcXHcrKSspIiwgIiIsIGxhYmF0dF9jbGVhbl9wcmUpCmxhYmF0dF9jbGVhbl9wcmUgPC0gZ3N1YigiQFxcdysiLCAiIiwgbGFiYXR0X2NsZWFuX3ByZSkKbGFiYXR0X2NsZWFuX3ByZSA8LSBnc3ViKCJbWzpwdW5jdDpdXSIsICIiLCBsYWJhdHRfY2xlYW5fcHJlKQpsYWJhdHRfY2xlYW5fcHJlIDwtIGdzdWIoIltbOmRpZ2l0Ol1dIiwgIiIsIGxhYmF0dF9jbGVhbl9wcmUpCmxhYmF0dF9jbGVhbl9wcmUgPC0gZ3N1YigiaHR0cFxcdysiLCAiIiwgbGFiYXR0X2NsZWFuX3ByZSkKbGFiYXR0X2NsZWFuX3ByZSA8LSBnc3ViKCJbIFx0XXsyLH0iLCAiIiwgbGFiYXR0X2NsZWFuX3ByZSkKbGFiYXR0X2NsZWFuX3ByZSA8LSBnc3ViKCJeXFxzK3xcXHMrJCIsICIiLCBsYWJhdHRfY2xlYW5fcHJlKQoKbGFiYXR0X2NvcnB1c19wcmUgPC0gQ29ycHVzKFZlY3RvclNvdXJjZShsYWJhdHRfY2xlYW5fcHJlKSkKbGFiYXR0X2NvcnB1c19wcmUgPC0gdG1fbWFwKGxhYmF0dF9jb3JwdXNfcHJlLCByZW1vdmVQdW5jdHVhdGlvbikKbGFiYXR0X2NvcnB1c19wcmUgPC0gdG1fbWFwKGxhYmF0dF9jb3JwdXNfcHJlLCBjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKQpsYWJhdHRfY29ycHVzX3ByZSA8LSB0bV9tYXAobGFiYXR0X2NvcnB1c19wcmUsIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuZ2xpc2giKSkKbGFiYXR0X2NvcnB1c19wcmUgPC0gdG1fbWFwKGxhYmF0dF9jb3JwdXNfcHJlLCByZW1vdmVXb3JkcywgYygiYW1wIiwgIjJ5byIsICIzeW8iLCAiNHlvIikpCmxhYmF0dF9jb3JwdXNfcHJlIDwtIHRtX21hcChsYWJhdHRfY29ycHVzX3ByZSwgc3RyaXBXaGl0ZXNwYWNlKQoKcGFsIDwtIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikKcGFsIDwtIHBhbFstKDE6NCldCnNldC5zZWVkKDEyMykKCndvcmRjbG91ZCh3b3JkcyA9IGxhYmF0dF9jb3JwdXNfcHJlLCBzY2FsZT1jKDUsMC4xKSwgbWF4LndvcmRzPTI1LCByYW5kb20ub3JkZXI9RkFMU0UsIAogICAgICAgICAgcm90LnBlcj0wLjM1LCB1c2Uuci5sYXlvdXQ9RkFMU0UsIGNvbG9ycz1wYWwpCmBgYAoKIyMjIFBvaW50IEdyYXBocyBmb3IgUG9zdHMKRGlzcGxheXMgZW5nYWdlbWVudCBwZXIgcG9zdCB0byBmaW5kIG91dGxpZXJzLgpgYGB7ciBwb2ludF9lbmdhZ2VtZW50LCBpbmNsdWRlID0gVFJVRX0KcCA8LSBnZ3Bsb3QoYWxsX2NvbXBhbmllc190cywgYWVzKHggPSBtb250aCwgeSA9IHRvdGFsX2VuZ2FnZW1lbnQpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBmcm9tX25hbWUpKSArCiAgeGxhYigiWWVhciIpICsgeWxhYigiVG90YWwgRW5nYWdlbWVudCIpICsgCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLCAKICAgICAgICBsZWdlbmQucG9zaXRpb249YygwLjE4LCAwLjc3KSwgCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGw9YWxwaGEoJ2dyYXknLCAwKSkpCnBsb3QocCkKYGBgCgojIyMgVG90YWwgRW5nYWdlbWVudCBMaW5lCmBgYHtyIHRvdGFsX2VuZ2FnZW1lbnRfbGluZSwgaW5jbHVkZSA9IFRSVUV9CiMgcSA8LSBhZ2dyZWdhdGUoYWxsX2NvbXBhbmllc190cyR0b3RhbF9lbmdhZ2VtZW50fmFsbF9jb21wYW5pZXNfdHMkbW9udGgrCiMgICAgICAgICAgICAgICAgICBhbGxfY29tcGFuaWVzX3RzJGZyb21fbmFtZSwKIyAgICAgICAgICAgICAgICBGVU49c3VtKQojIAojIGdncGxvdChxLCBhZXMoeCA9IHEkYGFsbF9jb21wYW5pZXNfdHMkbW9udGhgLCB5ID0gcSRgYWxsX2NvbXBhbmllc190cyR0b3RhbF9lbmdhZ2VtZW50YCkpICsKIyAgIGdlb21fbGluZShhZXMoY29sb3I9cSRgYWxsX2NvbXBhbmllc190cyRmcm9tX25hbWVgKSkgKwojICAgeWxhYigiVG90YWwgRW5nYWdlbWVudCIpICsgeGxhYigiWWVhciIpICsKIyAgIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksIAojICAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLCAKIyAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbj1jKDAuMTgsIDAuNzcpLCAKIyAgICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsPWFscGhhKCdncmF5JywgMCkpKQpgYGAKCmBgYHtyfQoKYGBgCgoKIyMjIEVuZ2FnZW1lbnQgYnkgQ29tcGFueQpgYGB7ciBlbmdhZ2VtZW50X2J5X2NvbXBhbnksIGluY2x1ZGUgPSBUUlVFfQojIyMgbW9sc29uIENvbnRlbnQgT3ZlciBUaW1lICMjIwp0IDwtIGFsbF9jb21wYW5pZXNfdHMgJT4lCiAgZmlsdGVyKC4sIGZyb21fbmFtZSA9PSAiTW9sc29uIENhbmFkaWFuIikKdCA8LSBkYXRhLmZyYW1lKHRhYmxlKHQkbW9udGgsIHQkdHlwZSkpCgp0JFZhcjEgPC0gZGF0ZSh0JFZhcjEpCmdncGxvdCh0LCBhZXMoeCA9IFZhcjEsIHkgPSBGcmVxLCBncm91cCA9IFZhcjIpKSArCiAgZ2VvbV9saW5lKGFlcyhjb2xvcj1WYXIyKSkgKwogIGdndGl0bGUoJ01vbHNvbiBFbmdhZ2VtZW50KEZhY2Vib29rKScpICsKICB4bGFiKCJZZWFyIikgKyB5bGFiKCJQb3N0IEZyZXF1ZW5jeSIpICsKICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksIAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj1jKDAuMTgsIDAuNzcpLCAKICAgICAgICBsZWdlbmQuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbD1hbHBoYSgnZ3JheScsIDApKSkKYGBgCgoKCgoKYGBge3J9CiNUUklTVEVOJ1MgR1JBUEhTISEKI0xhYmF0dCBDb250ZW50IE92ZXIgVGltZQoKIyMjIExhYmF0dCBDb250ZW50IE92ZXIgVGltZSAjIyMKdCA8LSBhbGxfY29tcGFuaWVzX3RzICU+JQogIGZpbHRlciguLCBmcm9tX25hbWUgPT0gIkxhYmF0dCBVU0EiKQp0IDwtIGRhdGEuZnJhbWUodGFibGUodCRtb250aCwgdCR0eXBlKSkKCnQkVmFyMSA8LSBkYXRlKHQkVmFyMSkKZ2dwbG90KHQsIGFlcyh4ID0gVmFyMSwgeSA9IEZyZXEsIGdyb3VwID0gVmFyMikpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yPVZhcjIpKSArCiAgZ2d0aXRsZSgnTGFiYXR0IEZhY2Vib29rIEFjdGl2aXR5KEZhY2Vib29rKScpICsKICB4bGFiKCJZZWFyIikgKyB5bGFiKCJQb3N0IEZyZXF1ZW5jeSIpICsKICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksIAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj1jKDAuMTgsIDAuNzcpLCAKICAgICAgICBsZWdlbmQuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbD1hbHBoYSgnZ3JheScsIDApKSkKYGBgCgpgYGB7cn0KI0xhYmF0dCBDb250ZW50IE92ZXIgVGltZQoKI01pY2hlbG9iVUxUUkEgQ29udGVudCBPdmVyIFRpbWUgIyMjCnQgPC0gYWxsX2NvbXBhbmllc190cyAlPiUKICBmaWx0ZXIoLiwgZnJvbV9uYW1lID09ICJNaWNoZWxvYiBVTFRSQSIpCnQgPC0gZGF0YS5mcmFtZSh0YWJsZSh0JG1vbnRoLCB0JHR5cGUpKQoKdCRWYXIxIDwtIGRhdGUodCRWYXIxKQpnZ3Bsb3QodCwgYWVzKHggPSBWYXIxLCB5ID0gRnJlcSwgZ3JvdXAgPSBWYXIyKSkgKwogIGdlb21fbGluZShhZXMoY29sb3I9VmFyMikpICsKICBnZ3RpdGxlKCdNaWNoZWxvYiBVTFRSQSBFbmdhZ2VtZW50KEZhY2Vib29rKScpICsKICB4bGFiKCJZZWFyIikgKyB5bGFiKCJQb3N0IEZyZXF1ZW5jeSIpICsKICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksIAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj1jKDAuMTgsIDAuNzcpLCAKICAgICAgICBsZWdlbmQuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbD1hbHBoYSgnZ3JheScsIDApKSkKYGBgCgoKLSBJcyB0aGlzIHRydWU/ICBUT0RPOiBWZXJpZnkgdGhhdCB0aGVzZSBhcmUgdGhlIG9ubHkgY29udGVudCB0eXBlcyBmb3IgTW9sc29uLgoKYGBge3J9CiNMYWJhdHQgQ29udGVudCBPdmVyIFRpbWUKCiNCdWQgTGlnaHQgQ29udGVudCBPdmVyIFRpbWUgIyMjCnQgPC0gYWxsX2NvbXBhbmllc190cyAlPiUKICBmaWx0ZXIoLiwgZnJvbV9uYW1lID09ICJCdWQgTGlnaHQiKQp0IDwtIGRhdGEuZnJhbWUodGFibGUodCRtb250aCwgdCR0eXBlKSkKCnQkVmFyMSA8LSBkYXRlKHQkVmFyMSkKZ2dwbG90KHQsIGFlcyh4ID0gVmFyMSwgeSA9IEZyZXEsIGdyb3VwID0gVmFyMikpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yPVZhcjIpKSArCiAgZ2d0aXRsZSgnQnVkIExpZ2h0IEVuZ2FnZW1lbnQoRmFjZWJvb2spJykgKwogIHhsYWIoIlllYXIiKSArIHlsYWIoIlBvc3QgRnJlcXVlbmN5IikgKwogIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksIAogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwgCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPWMoMC4xOCwgMC43NyksIAogICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsPWFscGhhKCdncmF5JywgMCkpKQpgYGAKCgoKIyMjIFB1bGxpbmcgI2hhc3RhZ3MKCkkgZm91bmQgYW4gZXhhbXBsZSBvbiBbU3RhY2tvdmVyZmxvd10oaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8yNzE2ODIyNi9leHRyYWN0aW5nLWhhc2h0YWdzLWZyb20tdHdlZXRzKQoKPCEtLSA+IHR3ZWV0cyA8LSBjKCJOZXcgUiBqb2I6IFN0YXRpc3RpY2FsIGFuZCBNZXRob2RvbG9naWNhbCBDb25zdWx0YW50IGF0IHRoZSBDZW50ZXIgZm9yIE9wZW4gU2NpZW5jZSBodHRwOi8vd3d3LnItdXNlcnMuY29tL2pvYnMvc3RhdGlzdGljYWwtbWV0aG9kb2xvZ2ljYWwtY29uc3VsdGFudC1jZW50ZXItb3Blbi1zY2llbmNlLyDigKYgI3JzdGF0cyAjam9icyIsIk5ldyBSIGpvYjogUmVzZWFyY2ggRW5naW5lZXIvQXBwbGllZCBSZXNlYXJjaGVyIGF0IGVCYXkgaHR0cDovL3d3dy5yLXVzZXJzLmNvbS9qb2JzL3Jlc2VhcmNoLWVuZ2luZWVyYXBwbGllZC1yZXNlYXJjaGVyLWViYXkvIOKApiAjcnN0YXRzICNqb2JzIikgLS0+CjwhLS0gPiBtYXRjaCA8LSByZWdtYXRjaGVzKHR3ZWV0cyxncmVnZXhwcigiI1tbOmFsbnVtOl1dKyIsdHdlZXRzKSkgLS0+CjwhLS0gPiBtYXRjaCAtLT4KPCEtLSBbWzFdXSAtLT4KPCEtLSBbMV0gIiNyc3RhdHMiICIjam9icyIgICAtLT4KCjwhLS0gW1syXV0gLS0+CjwhLS0gWzFdICIjcnN0YXRzIiAiI2pvYnMiICAgLS0+CjwhLS0gPiB1bmxpc3QobWF0Y2gpIC0tPgo8IS0tIFsxXSAiI3JzdGF0cyIgIiNqb2JzIiAgICIjcnN0YXRzIiAiI2pvYnMiICAgLS0+CgoKIyMjIEV4cGVyaW1lbnQgd2l0aCBIYXNodGFnIGV4dHJhY3Rpb24KYGBge3IgaGFzaEV4dHJhY3QsIGV2YWw9RkFMU0V9CiMgTGFiYXR0VVNBX3RpbWVsaW5lICU+JSAKIyAgIGZpbHRlcigpCiMgCiMgCiMgdHdlZXRzIDwtIExhYmF0dFVTQV90aW1lbGluZSR0ZXh0CiMgbWF0Y2ggPC0gcmVnbWF0Y2hlcyh0d2VldHMsZ3JlZ2V4cHIoIiNbWzphbG51bTpdXSsiLHR3ZWV0cykpCiMgCiMgIyBDb252ZXJ0IHRoZSBsaXN0IHRvIGEgY29ycHVzCiMgIyBuZXdfY29ycHVzIDwtIGFzLlZDb3JwdXMobmV3X2xpc3QpICBmcm9tIFN0YWNrb3ZlcmZsb3cgKGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzQwNjE5MTIvaG93LXRyYW5zZm9ybS1hLWxpc3QtaW50by1hLWNvcnB1cy1pbi1yKQojIAojIG5ld19jb3JwdXMgPC0gYXMuVkNvcnB1cyhtYXRjaCkKIyBjbGFzcyhuZXdfY29ycHVzKQojIGluc3BlY3QobmV3X2NvcnB1cykKIyAKIyBFbnN1cmVQYWNrYWdlIDwtIGZ1bmN0aW9uKHgpIHsKIyAgICMgRW5zdXJlUGFja2FnZSh4KSAtIEluc3RhbGxzIGFuZCBsb2FkcyBhIHBhY2thZ2UgaWYgbmVjZXNzYXJ5CiMgICAjIEFyZ3M6CiMgICAjICAgeDogbmFtZSBvZiBwYWNrYWdlCiMgCiMgICB4IDwtIGFzLmNoYXJhY3Rlcih4KQojICAgaWYgKCFyZXF1aXJlKHgsIGNoYXJhY3Rlci5vbmx5PVRSVUUpKSB7CiMgICAgIGluc3RhbGwucGFja2FnZXMocGtncz14LCByZXBvcz0iaHR0cDovL2NyYW4uci1wcm9qZWN0Lm9yZyIpCiMgICAgIHJlcXVpcmUoeCwgY2hhcmFjdGVyLm9ubHk9VFJVRSkKIyAgIH0KIyB9CiMgCiMgTWFrZVdvcmRDbG91ZCA8LSBmdW5jdGlvbihjb3JwdXMpIHsKIyAgICMgTWFrZSBhIHdvcmQgY2xvdWQKIyAgICMKIyAgICMgQXJnczoKIyAgICMgICB0ZXh0VmVjOiBhIHRleHQgdmVjdG9yCiMgICAjCiMgICAjIFJldHVybnM6CiMgICAjICAgQSB3b3JkIGNsb3VkIGNyZWF0ZWQgZnJvbSB0aGUgdGV4dCB2ZWN0b3IKIyAgIAojICAgRW5zdXJlUGFja2FnZSgidG0iKQojICAgRW5zdXJlUGFja2FnZSgid29yZGNsb3VkIikKIyAgIEVuc3VyZVBhY2thZ2UoIlJDb2xvckJyZXdlciIpCiMgICAKIyAgIGNvcnB1cyA8LSB0bV9tYXAoY29ycHVzLCBmdW5jdGlvbih4KSB7CiMgICAgIHJlbW92ZVdvcmRzKHgsIGMoInZpYSIsICJydCIsICJtdCIpKQojICAgfSkKIyAgIAojICAgYXAudGRtIDwtIFRlcm1Eb2N1bWVudE1hdHJpeChjb3JwdXMpCiMgICBhcC5tIDwtIGFzLm1hdHJpeChhcC50ZG0pCiMgICBhcC52IDwtIHNvcnQocm93U3VtcyhhcC5tKSwgZGVjcmVhc2luZz1UUlVFKQojICAgYXAuZCA8LSBkYXRhLmZyYW1lKHdvcmQgPSBuYW1lcyhhcC52KSwgZnJlcT1hcC52KQojICAgdGFibGUoYXAuZCRmcmVxKQojICAgcGFsMiA8LSBicmV3ZXIucGFsKDgsICJEYXJrMiIpCiMgICAKIyAgIHdvcmRjbG91ZChhcC5kJHdvcmQsIGFwLmQkZnJlcSwgCiMgICAgICAgICAgICAgc2NhbGU9Yyg4LCAuMiksIG1pbi5mcmVxID0gMywgCiMgICAgICAgICAgICAgbWF4LndvcmRzID0gSW5mLCByYW5kb20ub3JkZXIgPSBGQUxTRSwgCiMgICAgICAgICAgICAgcm90LnBlciA9IC4xNSwgY29sb3JzID0gcGFsMikKIyB9CiMgCiMgTWFrZVdvcmRDbG91ZChuZXdfY29ycHVzKQpgYGAKCiMjIyBNb3NhaWMgUGxvdCBFeHBlcmltZW50CgoKLSBbIF0gVE9ETzogRnVsbCB0aW1lc2VyaWVzIG9mIHRvdGFsIGVuZyBieSBicmFuZC4gIChUbyBsb29rIGZvciBzZWFzb25hbGl0eSkgLSBpZiBzcG9ydHMgYXJlIGEgZHJpdmVyIHRoYW4gc2Vhc29uYWxpdHkgbWlnaHQgYmUgaW1wb3J0YW50IAoKYGBge3J9CiMgcCA8LSB1bmZpbHRlcmVkX3RzICU+JQojICAgc3VtbWFyaXNlKGpkID0gZG95KHRpbWVzdGFtcCkpICU+JQojICAgZ3JvdXBfYnkoamQpICU+JQojICAgZ2dwbG90KGFlcyhmYWN0b3IoamQpLHRvdGFsX2VuZ2FnZW1lbnQpKSArCiMgICBnZW9tX2JveHBsb3QoKSArIAojICAgZmFjZXRfZ3JpZCh+IGZyb21fbmFtZSkKIyBwbG90KHApCmBgYAoKLSBbIF0gUG9wdWxhdGUgYSB0YWJsZSBvZiB0b3AgcGVyZm9ybWluZyBwb3N0cyBhbmQgbG93IHBlcmZvcm1pbmcgcG9zdHMgLSBUcmlzdGVuIGNhbiBwdWxsIHNob3Qgb2YgdHdlZXRzIGZvciBkaXNjdXNzaW9uCi0gWyBdIENyZWF0ZSBhIGRhdGEuZnJhbWUgd2l0aCB0aGVzZSBjb2x1bW5zIGJyYW5kLCBkYXRhLCB0d2VldCwgZW5nYWdlbWVudCAoSSB0aGluayB0aGlzIGlzIGEgc3Vic2V0IG9mIGFsbF9jb21wYW5pZXMpCgoKLSBbIF0gc3VtbWFyeSB0YWJsZSBvZiBicmFuZCwgbW9udGgsIHRvdEVuZywgc2VlIGV4YW1wbGVzOmh0dHA6Ly9sZW9uYXdpY3ouZ2l0aHViLmlvL0h0bWxXaWRnZXRFeGFtcGxlcy9leF9kdF9zcGFya2xpbmUuaHRtbAoKCmBgYHtyfQphbGxfY29tcGFuaWVzX3RzICU+JQogIHNlbGVjdChmcm9tX25hbWUsIHRpbWVzdGFtcCwgdG90YWxfZW5nYWdlbWVudCkgJT4lCiAgZ3JvdXBfYnkoZnJvbV9uYW1lLCBtb250aCh0aW1lc3RhbXApLCB5ZWFyKHRpbWVzdGFtcCkpICU+JQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSwgCiAgICAgICAgICAgIGVuZ2FnZW1lbnQgPSBzdW0odG90YWxfZW5nYWdlbWVudCkpICU+JQogIGdncGxvdCguLCBhZXMoeSA9IGxvZyhlbmdhZ2VtZW50KSwgeCA9IGxvZyhjb3VudCksIGNvbG91ciA9IGZyb21fbmFtZSkpICsKICBnZW9tX3BvaW50KCkgKwogIHhsYWIoJ1Bvc3QgQWN0aXZpdHknKSArIHlsYWIoJ0VuZ2FnZW1lbnQnKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSwgbWV0aG9kID0gImxtIikgKwogICNnZW9tX3Ntb290aChzZSA9IEZBTFNFKQogIGdndGl0bGUoIkVuZ2FnZW1lbnQgdnMgUG9zdCBBY2l0aXZpdHkoRmFjZWJvb2spIikKYGBgCgoKCgoKYGBge3J9CmFsbF9jb21wYW5pZXNfdHMgJT4lCiAgI2ZpbHRlcihmcm9tX25hbWUgIT0gIkJ1ZCBMaWdodCIgKSAlPiUKICAjZmlsdGVyKGZyb21fbmFtZSAhPSAiTWljaGVsb2IgVUxUUkEiKSAlPiUKICBzZWxlY3QoZnJvbV9uYW1lLCB0aW1lc3RhbXAsIHRvdGFsX2VuZ2FnZW1lbnQpICU+JQogIGdyb3VwX2J5KGZyb21fbmFtZSwgbW9udGgodGltZXN0YW1wKSwgeWVhcih0aW1lc3RhbXApKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCksCiAgICAgICAgICAgIGVuZ2FnZW1lbnQgPSBzdW0odG90YWxfZW5nYWdlbWVudCkpICU+JQogIGdncGxvdCguLCBhZXMoeSA9IGxvZyhlbmdhZ2VtZW50KSwgeCA9IGxvZyhjb3VudCksIGNvbG91ciA9IGZyb21fbmFtZSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UsIG1ldGhvZCA9ICJsbSIpICsKICBnZ3RpdGxlKCJFbmdhZ2VtZW50IHZzIFBvc3QgQWNpdGl2aXR5KEZhY2Vib29rKSIpICsKICB5bGFiKCJUb3RhbCBFbmdhZ2VtZW50IikgKyB4bGFiKCJUb3RhbCBNb250aGx5IFBvc3RzIikKYGBgCgotIFRoZXJlIGlzIGEgcG9zaXRpdmUgcmVsYXRpb25zaGlwIGJldHdlZW4gcG9zdCBhY3Rpdml0eSAoaWUgY291bnRzKSBhbmQgdG90YWwgZW5nYWdlbWVudC4gIAoKYGBge3J9CmFsbF9jb21wYW5pZXNfdHMgJT4lCiAgZmlsdGVyKGZyb21fbmFtZSA9PSAiTGFiYXR0IFVTQSIgKSAlPiUKICBzZWxlY3QoZnJvbV9uYW1lLCB0aW1lc3RhbXAsIHR5cGUsIHRvdGFsX2VuZ2FnZW1lbnQpICU+JQogIGdyb3VwX2J5KGZyb21fbmFtZSwgbW9udGgodGltZXN0YW1wKSwgeWVhcih0aW1lc3RhbXApLCB0eXBlKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCksCiAgICAgICAgICAgIGVuZ2FnZW1lbnQgPSBzdW0odG90YWxfZW5nYWdlbWVudCkpICU+JQogIGdncGxvdCguLCBhZXMoeSA9IGxvZyhlbmdhZ2VtZW50KSwgeCA9IGxvZyhjb3VudCksIGNvbG91ciA9IHR5cGUpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFLCBtZXRob2QgPSAibG0iKSArCiAgZ2d0aXRsZSgiUG9zdCBFZmZpY2FjeSBieSB0eXBlIGZvciBMYWJhdHQgVVNBKEZhY2Vib29rKSIpICsKICB5bGFiKCJUb3RhbCBFbmdhZ2VtZW50IikgKyB4bGFiKCJUb3RhbCBNb250aGx5IFBvc3RzIikKCgpgYGAKCi0gW1hdIFRPRCB2cyBlbmdhZ2VtZW50IHNpbWlsYXIgdG8gcG9zdCBhY3Rpdml0eSB2cyBFbmdhZ2VtZW50CgpgYGB7cn0KYWxsX2NvbXBhbmllc190cyAlPiUKICBmaWx0ZXIoZnJvbV9uYW1lID09ICJMYWJhdHQgVVNBIiApICU+JQogIHNlbGVjdChmcm9tX25hbWUsIHRvZCwgdG90YWxfZW5nYWdlbWVudCkgJT4lCiAgZ2dwbG90KC4sIGFlcyh5ID0gdG90YWxfZW5nYWdlbWVudCwgeCA9IGZhY3Rvcih0b2QpLCBjb2xvdXIgPSBmcm9tX25hbWUpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHlsaW0oYygwLDIwMDApKSArCiAgZ2d0aXRsZSgiUG9zdCBFZmZpY2FjeSBieSB0eXBlIGZvciBMYWJhdHQgVVNBKEZhY2Vib29rKSIpICsKICB5bGFiKCJUb3RhbCBFbmdhZ2VtZW50IikgKyB4bGFiKCJUaW1lIG9mIERheSIpCgoKYGBgCgoKIyMjIEtldmlucyBRdWVzdGlvbnMgCmBgYHtyIGtldmluc19xdWVzdGlvbnMsIGluY2x1ZGUgPSBUUlVFfQojIGxvYWQoJ3Byb2Nlc3NlZF9kYXRhL2J1ZF9mYi5SRGF0YScpCiMgYnVkJHRvdGFsX2VuZ2FnZW1lbnQgPC0gcm93U3VtcyhidWRbLDk6MTFdKQojIHogPC0gYnVkICU+JQojICAgYXJyYW5nZShkZXNjKHRvdGFsX2VuZ2FnZW1lbnQpKQojIGhlYWQoeikKIyBVcGRhdGVkIHVwc3RyZWFtCmBgYAoKCiMjIFR3aXR0ZXIKYGBge3IgdHdpdHRlciBzZW50aW1lbnQsIGluY2x1ZGUgPSBUUlVFfQp0ZXh0X2NsZWFuIDwtIGZ1bmN0aW9uKGNsZWFubGluZXNzKSB7CiAgY2xlYW5saW5lc3MgPC0gc3RyX3JlcGxhY2VfYWxsKGNsZWFubGluZXNzLCAiQFxcdysiLCAiIikKICBjbGVhbmxpbmVzcyA8LSBnc3ViKCImYW1wIiwgIiIsIGNsZWFubGluZXNzKQogIGNsZWFubGluZXNzIDwtIGdzdWIoIihSVHx2aWEpKCg/OlxcYlxcVypAXFx3KykrKSIsICIiLCBjbGVhbmxpbmVzcykKICBjbGVhbmxpbmVzcyA8LSBnc3ViKCJAXFx3KyIsICIiLCBjbGVhbmxpbmVzcykKICBjbGVhbmxpbmVzcyA8LSBnc3ViKCJbWzpwdW5jdDpdXSIsICIiLCBjbGVhbmxpbmVzcykKICBjbGVhbmxpbmVzcyA8LSBnc3ViKCJbWzpkaWdpdDpdXSIsICIiLCBjbGVhbmxpbmVzcykKICBjbGVhbmxpbmVzcyA8LSBnc3ViKCJodHRwXFx3KyIsICIiLCBjbGVhbmxpbmVzcykKICBjbGVhbmxpbmVzcyA8LSBnc3ViKCJbIFx0XXsyLH0iLCAiIiwgY2xlYW5saW5lc3MpCiAgY2xlYW5saW5lc3MgPC0gZ3N1YigiXlxccyt8XFxzKyQiLCAiIiwgY2xlYW5saW5lc3MpCiAgcmV0dXJuKGNsZWFubGluZXNzKQp9CgpMYWJhdHRVU0FfdGltZWxpbmUkc2VudGltZW50IDwtIGxhcHBseSh0ZXh0X2NsZWFuKExhYmF0dFVTQV90aW1lbGluZSR0ZXh0KSwgZ2V0X25yY19zZW50aW1lbnQpCmxhYmF0dF9zZW50aW1lbnQgPC0gZGF0YS5mcmFtZSgnY3JlYXRlZCcgPSBMYWJhdHRVU0FfdGltZWxpbmUkY3JlYXRlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICd0ZXh0JyA9IExhYmF0dFVTQV90aW1lbGluZSR0ZXh0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3NlbnRpbWVudCcgPSBhcy5jaGFyYWN0ZXIoTGFiYXR0VVNBX3RpbWVsaW5lJHNlbnRpbWVudCkpCmxhYmF0dF9zZW50aW1lbnQkc2NvcmUgPC0gZ2V0X3NlbnRpbWVudChhcy5jaGFyYWN0ZXIodGV4dF9jbGVhbihsYWJhdHRfc2VudGltZW50JHRleHQpKSkgJT4lIGFzLm51bWVyaWMoKQoKbGFiYXR0X3NlbnRpbWVudCAlPiUKICBnZ3Bsb3QoYWVzKGFzX2RhdGUoY3JlYXRlZCksIHNjb3JlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbG91ckxpc3QpICsKICBzY2FsZV94X2RhdGUobmFtZSA9ICdcbkRhdGVzJywgYnJlYWtzID0gZGF0ZV9icmVha3MoIjMgbW9udGhzIiksIGxhYmVscyA9IGRhdGVfZm9ybWF0KCIlWS0lYiIpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAiU2VudGltZW50IFNjb3JlXG4iLCBicmVha3MgPSBzZXEoLTUsIDUsIGJ5ID0gMSkpICsgdGhlbWVfYncoKSArCiAgZ2d0aXRsZSgnTGFiYXR0IFVTQSBTZW50aW1lbnQoVHdpdHRlciknKQoKCk1vbHNvbl9DYW5hZGlhbl90aW1lbGluZSRzZW50aW1lbnQgPC0gbGFwcGx5KHRleHRfY2xlYW4oTW9sc29uX0NhbmFkaWFuX3RpbWVsaW5lJHRleHQpLCBnZXRfbnJjX3NlbnRpbWVudCkKbW9sc29uX3NlbnRpbWVudCA8LSBkYXRhLmZyYW1lKCdjcmVhdGVkJyA9IE1vbHNvbl9DYW5hZGlhbl90aW1lbGluZSRjcmVhdGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3RleHQnID0gTW9sc29uX0NhbmFkaWFuX3RpbWVsaW5lJHRleHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnc2VudGltZW50JyA9IGFzLmNoYXJhY3RlcihNb2xzb25fQ2FuYWRpYW5fdGltZWxpbmUkc2VudGltZW50KSkKbW9sc29uX3NlbnRpbWVudCRzY29yZSA8LSBnZXRfc2VudGltZW50KGFzLmNoYXJhY3Rlcih0ZXh0X2NsZWFuKG1vbHNvbl9zZW50aW1lbnQkdGV4dCkpKSAlPiUgYXMubnVtZXJpYygpCgptb2xzb25fc2VudGltZW50ICU+JQogIGdncGxvdChhZXMoYXNfZGF0ZShjcmVhdGVkKSwgc2NvcmUpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3VyTGlzdCkgKwogIHNjYWxlX3hfZGF0ZShuYW1lID0gJ1xuRGF0ZXMnLCBicmVha3MgPSBkYXRlX2JyZWFrcygiMyBtb250aHMiKSwgbGFiZWxzID0gZGF0ZV9mb3JtYXQoIiVZLSViIikpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJTZW50aW1lbnQgU2NvcmVcbiIsIGJyZWFrcyA9IHNlcSgtNSwgNSwgYnkgPSAxKSkgKyB0aGVtZV9idygpICsKICBnZ3RpdGxlKCdNb2xzb24gQ2FuYWRpYW4gU2VudGltZW50KFR3aXR0ZXIpJykKCmJ1ZGxpZ2h0X3RpbWVsaW5lJHNlbnRpbWVudCA8LSBsYXBwbHkodGV4dF9jbGVhbihidWRsaWdodF90aW1lbGluZSR0ZXh0KSwgZ2V0X25yY19zZW50aW1lbnQpCmJ1ZGxpZ2h0X3NlbnRpbWVudCA8LSBkYXRhLmZyYW1lKCdjcmVhdGVkJyA9IGJ1ZGxpZ2h0X3RpbWVsaW5lJGNyZWF0ZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAndGV4dCcgPSBidWRsaWdodF90aW1lbGluZSR0ZXh0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3NlbnRpbWVudCcgPSBhcy5jaGFyYWN0ZXIoYnVkbGlnaHRfdGltZWxpbmUkc2VudGltZW50KSkKYnVkbGlnaHRfc2VudGltZW50JHNjb3JlIDwtIGdldF9zZW50aW1lbnQoYXMuY2hhcmFjdGVyKHRleHRfY2xlYW4oYnVkbGlnaHRfc2VudGltZW50JHRleHQpKSkgJT4lIGFzLm51bWVyaWMoKQoKYnVkbGlnaHRfc2VudGltZW50ICU+JQogIGdncGxvdChhZXMoYXNfZGF0ZShjcmVhdGVkKSwgc2NvcmUpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3VyTGlzdCkgKwogIHNjYWxlX3hfZGF0ZShuYW1lID0gJ1xuRGF0ZXMnLCBicmVha3MgPSBkYXRlX2JyZWFrcygiMyBtb250aHMiKSwgbGFiZWxzID0gZGF0ZV9mb3JtYXQoIiVZLSViIikpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJTZW50aW1lbnQgU2NvcmVcbiIsIGJyZWFrcyA9IHNlcSgtNSwgNSwgYnkgPSAxKSkgKyB0aGVtZV9idygpICsKICBnZ3RpdGxlKCdCdWQgTGlnaHQgU2VudGltZW50KFR3aXR0ZXIpJykKCk1pY2hlbG9iVUxUUkFfdGltZWxpbmUkc2VudGltZW50IDwtIGxhcHBseSh0ZXh0X2NsZWFuKE1pY2hlbG9iVUxUUkFfdGltZWxpbmUkdGV4dCksIGdldF9ucmNfc2VudGltZW50KQptaWNoZWxvYl9zZW50aW1lbnQgPC0gZGF0YS5mcmFtZSgnY3JlYXRlZCcgPSBNaWNoZWxvYlVMVFJBX3RpbWVsaW5lJGNyZWF0ZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAndGV4dCcgPSBNaWNoZWxvYlVMVFJBX3RpbWVsaW5lJHRleHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnc2VudGltZW50JyA9IGFzLmNoYXJhY3RlcihNaWNoZWxvYlVMVFJBX3RpbWVsaW5lJHNlbnRpbWVudCkpCm1pY2hlbG9iX3NlbnRpbWVudCRzY29yZSA8LSBnZXRfc2VudGltZW50KGFzLmNoYXJhY3Rlcih0ZXh0X2NsZWFuKG1pY2hlbG9iX3NlbnRpbWVudCR0ZXh0KSkpICU+JSBhcy5udW1lcmljKCkKCm1pY2hlbG9iX3NlbnRpbWVudCAlPiUKICBnZ3Bsb3QoYWVzKGFzX2RhdGUoY3JlYXRlZCksIHNjb3JlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbG91ckxpc3QpICsKICBzY2FsZV94X2RhdGUobmFtZSA9ICdcbkRhdGVzJywgYnJlYWtzID0gZGF0ZV9icmVha3MoIjMgbW9udGhzIiksIGxhYmVscyA9IGRhdGVfZm9ybWF0KCIlWS0lYiIpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAiU2VudGltZW50IFNjb3JlXG4iLCBicmVha3MgPSBzZXEoLTUsIDUsIGJ5ID0gMSkpICsgdGhlbWVfYncoKSArCiAgZ2d0aXRsZSgnTWljaGVsb2IgVUxUUkEgU2VudGltZW50KFR3aXR0ZXIpXG4nKQoKYGBgCgo=